/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use serde_json::json;
use std::fs;
use std::collections::HashMap;
use attestation::{
    chain::{AttestationContext, attestation_chain::AttestationHandler},
    chain::handlers::{
        nonce_processing_handler::NonceProcessingHandler,
        custom_policy_handler::CustomPolicyHandler,
        evidence_response_handler::EvidenceResponseHandler,
        export_policy_handler::ExportPolicyHandler,
        nonce_validation_handler::NonceValidationHandler,
    },
    entities::attest_request::{AttestRequest, Evidence, Measurement},
    error::attestation_error::AttestationError,
    factory::token_factory::TokenFactory,
};
use plugin_manager::{PluginManager, PluginManagerInstance, ServiceHostFunctions, ServicePlugin, PluginError};
use async_trait::async_trait;
use serde_json::Value;
use config_manager::types::context::{CONFIG, AttestationService, Policy, ExportPolicyFile, ServerConfig, KeyManagement, TokenManagement, Cert, NonceConfig, RefValue};
use std::sync::Once;

#[cfg(test)]
mod tests {
    use super::*;

    fn setup() {
        let _ = common_log::init();
        setup_plugin_manager();
        setup_config();
    }

    fn create_test_policy() -> Policy {
        let export_policy_file = ExportPolicyFile {
            name: "test_attester".to_string(),
            path: format!("{}/tests/data/test_export_policy.rego", env!("CARGO_MANIFEST_DIR")),
        };
        Policy {
            export_policy_file: vec![export_policy_file],
            is_verify_policy_signature: false,
            single_user_policy_limit: 100,
            policy_content_size_limit: 1024000,
            query_user_policy_limit: 100,
        }
    }

    fn create_test_key_management() -> KeyManagement {
        KeyManagement {
            vault_get_key_url: "http://localhost:8200".to_string(),
            is_require_sign: false,
            key_ca_cert_path: "/tmp/ca.crt".to_string(),
            key_cli_key_path: "/tmp/client.key".to_string(),
            key_cli_cert_path: "/tmp/client.crt".to_string(),
        }
    }

    fn create_test_token_management() -> TokenManagement {
        TokenManagement {
            jku: "http://localhost:8080/jwks".to_string(),
            kid: "test-key-id".to_string(),
            exist_time: 3600000,
            iss: "test-issuer".to_string(),
            eat_profile: "test-profile".to_string(),
            mq_enabled: false,
            token_topic: "test-topic".to_string(),
            token_signing_algorithm: "RS256".to_string(),
        }
    }

    fn create_test_attestation_service() -> AttestationService {
        AttestationService {
            key_management: create_test_key_management(),
            token_management: create_test_token_management(),
            policy: create_test_policy(),
            ref_value: RefValue {
                single_user_ref_value_limit: 100,
            },
            cert: Cert {
                single_user_cert_limit: 100,
            },
            nonce: NonceConfig {
                nonce_valid_period: 300,
                nonce_bytes: 32,
            },
            plugins: vec![],
        }
    }

    fn setup_config() {
        static INIT: Once = Once::new();
        let _ = INIT.call_once(|| {
            let config = ServerConfig {
                attestation_common: None,
                attestation_service: create_test_attestation_service(),
            };
            let _ = CONFIG.set_instance(config);
        });
    }

    // Mock plugin for testing evidence verification
    #[allow(dead_code)]
    struct MockTestPlugin;

    impl plugin_manager::PluginBase for MockTestPlugin {
        fn plugin_type(&self) -> &str {
            "test_attester"
        }
    }

    #[async_trait]
    impl ServicePlugin for MockTestPlugin {
        fn get_sample_output(&self) -> Value {
            json!({
                "verification_result": true,
                "evidence_data": "test_evidence"
            })
        }

        async fn verify_evidence(
            &self,
            _user_id: &str,
            _node_id: Option<&str>,
            _evidence: &Value,
            _nonce: Option<&[u8]>,
        ) -> Result<Value, PluginError> {
            Ok(json!({
                "verification_result": true,
                "evidence_data": "test_evidence",
                "status": "verified"
            }))
        }
    }

    // Mock host functions for testing
    fn create_test_host_functions() -> ServiceHostFunctions {
        ServiceHostFunctions {
            validate_cert_chain: Box::new(|_, _, cert_data| Box::pin(async move {
                !cert_data.is_empty()
            })),
            get_unmatched_measurements: Box::new(|measured_values, _, _| Box::pin(async move {
                Ok(measured_values.clone())
            })),
            query_configuration: |s| if s == "test" { Some("test".to_string()) } else { Some("default".to_string()) },
        }
    }

    fn build_mock_plugin() -> Result<std::path::PathBuf, String> {
        use std::process::Command;
        use std::path::Path;
        
        // Get the path to the mock plugin source
        let manifest_dir = match std::env::var("CARGO_MANIFEST_DIR") {
            Ok(dir) => dir,
            Err(_) => return Err("CARGO_MANIFEST_DIR not set".to_string()),
        };
        
        // Use platform-agnostic path joining
        let mock_plugin_dir = Path::new(&manifest_dir).join("tests").join("mock_test_attester");
        
        if !mock_plugin_dir.exists() {
            return Err(format!("Mock plugin directory not found at {:?}", mock_plugin_dir));
        }
        
        // Check if Cargo.toml exists
        let cargo_toml = mock_plugin_dir.join("Cargo.toml");
        if !cargo_toml.exists() {
            return Err(format!("Cargo.toml not found at {:?}", cargo_toml));
        }
        
        // Build the mock plugin
        // Use platform-agnostic cargo command
        let cargo = if cfg!(windows) { "cargo.exe" } else { "cargo" };
        
        let output = match Command::new(cargo)
            .args(["build", "--release"])
            .current_dir(&mock_plugin_dir)
            .output() {
                Ok(output) => output,
                Err(e) => return Err(format!("Failed to execute cargo build: {}", e)),
            };
        
        if !output.status.success() {
            return Err(format!("Failed to build mock plugin: {}", 
                String::from_utf8_lossy(&output.stderr)));
        }
        
        // Return the path to the built plugin library using platform-specific naming
        let target_dir = mock_plugin_dir.join("target").join("release");
        
        let lib_filename = get_platform_lib_filename("mock_test_attester");
        
        let lib_path = target_dir.join(lib_filename);
        
        if !lib_path.exists() {
            return Err(format!("Plugin library not found at {:?}", lib_path));
        }
        
        Ok(lib_path)
    }

    fn get_platform_lib_filename(name: &str) -> String {
        #[cfg(target_os = "windows")]
        {
            format!("{}.dll", name)
        }
        
        #[cfg(target_os = "linux")]
        {
            format!("lib{}.so", name)
        }
        
        #[cfg(not(any(target_os = "windows", target_os = "linux")))]
        {
            compile_error!("Unsupported platform for tests")
        }
    }

    fn setup_plugin_manager() {
        let host_functions = create_test_host_functions();
        let manager = PluginManager::<dyn ServicePlugin, ServiceHostFunctions>::get_instance();
        
        if !manager.is_initialized() {
            // Build the mock plugin first
            let mock_plugin_path = match build_mock_plugin() {
                Ok(path) => path,
                Err(e) => {
                    eprintln!("Failed to build mock plugin: {}", e);
                    return;
                }
            };
            
            // Create plugin paths map with our compiled mock plugin
            let mut plugin_paths = HashMap::new();
            plugin_paths.insert("test_attester".to_string(), mock_plugin_path.to_string_lossy().to_string());
            
            manager.initialize(&plugin_paths, &host_functions);
        }
    }

    fn create_test_measurement() -> Measurement {
        let test_data = fs::read_to_string("tests/data/test_measurement.json")
            .expect("Failed to read test_measurement.json");
        let request: AttestRequest = serde_json::from_str(&test_data)
            .expect("Failed to parse test_measurement.json");
        request.measurements[0].clone()
    }

    fn create_test_evidence() -> Evidence {
        let measurement = create_test_measurement();
        measurement.evidences[0].clone()
    }

    fn create_test_context() -> AttestationContext {
        let token_factory = TokenFactory::new();
        let token_instance = token_factory.create_token("eat").expect("Failed to create token");
        AttestationContext::new(
            create_test_measurement(),
            create_test_evidence(),
            "test_user".to_string(),
            token_instance,
        )
    }

    // Tests for NonceProcessing
    mod nonce_processor_tests {
        use super::*;

        #[tokio::test]
        async fn test_process_nonce_should_succeed_with_valid_nonce() {
            setup();
            
            let processor = NonceProcessingHandler::new();
            let mut context = create_test_context();
            
            let result = processor.handle(&mut context).await;
            
            // With test data (nonce_type="ignore"), should succeed
            assert!(result.is_ok());
            // Should set nonce_bytes to None and aggregate_nonce_bytes to Some
            assert!(context.nonce_bytes.is_none());
            assert!(context.aggregate_nonce_bytes.is_some());
        }

        #[tokio::test]
        async fn test_process_nonce_should_fail_when_nonce_is_missing() {
            setup();
            
            let processor = NonceProcessingHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = None;
            context.measurement.nonce_type = Some("user".to_string()); // Set nonce_type to trigger validation
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_err());
            match result.unwrap_err() {
                AttestationError::NonceVerificationError(msg) => {
                    assert!(msg.contains("nonce") || msg.contains("Nonce"));
                }
                _ => panic!("Expected NonceVerificationError for missing nonce"),
            }
        }

        #[tokio::test]
        async fn test_process_nonce_should_succeed_with_empty_nonce() {
            setup();
            
            let processor = NonceProcessingHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = Some("".to_string());
            context.measurement.nonce_type = Some("user".to_string()); // Set nonce_type to trigger validation
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }
    }

    // Tests for CustomPolicy
    mod custom_policy_tests {
        use super::*;

        #[tokio::test]
        async fn test_process_custom_policy_should_succeed_without_policies() {
            setup();
            
            let processor = CustomPolicyHandler::new();
            let mut context = create_test_context();
            context.evidence.policy_ids = None;
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }

        #[tokio::test]
        async fn test_process_custom_policy_should_succeed_with_empty_policies() {
            setup();
            
            let processor = CustomPolicyHandler::new();
            let mut context = create_test_context();
            context.evidence.policy_ids = Some(vec![]);
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }

        #[tokio::test]
        async fn test_get_custom_policy_processor_name_should_return_valid_name() {
            let processor = CustomPolicyHandler::new();
            let name = processor.name();
            assert!(!name.is_empty());
            assert!(name.contains("Policy") || name.contains("Custom"));
        }
    }

    // Tests for EvidenceResponse
    mod evidence_response_tests {
        use super::*;

        #[tokio::test]
        async fn test_process_evidence_response_should_succeed() {
            setup();
            
            let processor = EvidenceResponseHandler::new();
            let mut context = create_test_context();
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }

        #[tokio::test]
        async fn test_get_evidence_response_processor_name_should_return_valid_name() {
            let processor = EvidenceResponseHandler::new();
            let name = processor.name();
            assert!(!name.is_empty());
        }
    }

    // Tests for ExportPolicy
    mod export_policy_tests {
        use super::*;

        #[tokio::test]
        async fn test_process_export_policy_should_succeed() {
            setup();
            
            let processor = ExportPolicyHandler::new();
            let mut context = create_test_context();
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }

        #[tokio::test]
        async fn test_get_export_policy_processor_name_should_return_valid_name() {
            let processor = ExportPolicyHandler::new();
            let name = processor.name();
            assert!(!name.is_empty());
        }
    }

    // Tests for NonceValidation
    mod nonce_validation_tests {
        use super::*;
        use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};

        fn create_valid_nonce_base64() -> String {
            let nonce_data = json!({
                "val": "test_nonce_value",
                "iat": 1234567890,
                "signature": "test_signature"
            });
            BASE64.encode(nonce_data.to_string().as_bytes())
        }

        fn create_invalid_base64_nonce() -> String {
            "invalid_base64_string!@#$%".to_string()
        }

        fn create_invalid_utf8_nonce() -> String {
            // Create invalid UTF-8 by encoding invalid bytes
            BASE64.encode(&[0xFF, 0xFE, 0xFD])
        }

        fn create_invalid_json_nonce() -> String {
            BASE64.encode("invalid json string".as_bytes())
        }

        #[tokio::test]
        async fn test_validate_nonce_should_succeed_with_user_type_and_valid_nonce() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = Some(create_valid_nonce_base64());
            context.measurement.nonce_type = Some("user".to_string());
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }

        #[tokio::test]
        async fn test_validate_nonce_should_fail_with_user_type_and_missing_nonce() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = None;
            context.measurement.nonce_type = Some("user".to_string());
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_err());
            match result.unwrap_err() {
                AttestationError::NonceVerificationError(msg) => {
                    assert!(msg.contains("nonce") || msg.contains("Nonce"));
                }
                _ => panic!("Expected NonceVerificationError for missing nonce"),
            }
        }

        #[tokio::test]
        async fn test_validate_nonce_should_succeed_with_ignore_type() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = None;
            context.measurement.nonce_type = Some("ignore".to_string());
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }

        #[tokio::test]
        async fn test_validate_nonce_should_succeed_with_ignore_type_and_nonce_present() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = Some(create_valid_nonce_base64());
            context.measurement.nonce_type = Some("ignore".to_string());
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }

        #[tokio::test]
        async fn test_validate_nonce_should_fail_with_verifier_type_and_missing_nonce() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = None;
            context.measurement.nonce_type = Some("verifier".to_string());
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_err());
            match result.unwrap_err() {
                AttestationError::NonceVerificationError(msg) => {
                    assert!(msg.contains("nonce") || msg.contains("Nonce"));
                }
                _ => panic!("Expected NonceVerificationError for missing nonce"),
            }
        }

        #[tokio::test]
        async fn test_validate_nonce_should_fail_with_invalid_base64_encoding() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = Some(create_invalid_base64_nonce());
            context.measurement.nonce_type = Some("verifier".to_string()); // Use verifier type to trigger validation
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_err());
            match result.unwrap_err() {
                AttestationError::NonceVerificationError(msg) => {
                    assert!(msg.contains("failed to decode base64 nonce"));
                }
                _ => panic!("Expected NonceVerificationError for invalid base64"),
            }
        }

        #[tokio::test]
        async fn test_validate_nonce_should_fail_with_invalid_utf8_string() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = Some(create_invalid_utf8_nonce());
            context.measurement.nonce_type = Some("verifier".to_string()); // Use verifier type to trigger validation
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_err());
            match result.unwrap_err() {
                AttestationError::NonceVerificationError(msg) => {
                    assert!(msg.contains("invalid nonce format"));
                }
                _ => panic!("Expected NonceVerificationError for invalid UTF-8"),
            }
        }

        #[tokio::test]
        async fn test_validate_nonce_should_fail_with_invalid_json_format() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = Some(create_invalid_json_nonce());
            context.measurement.nonce_type = Some("verifier".to_string()); // Use verifier type to trigger validation
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_err());
            match result.unwrap_err() {
                AttestationError::NonceVerificationError(msg) => {
                    assert!(msg.contains("invalid nonce format"));
                }
                _ => panic!("Expected NonceVerificationError for invalid JSON"),
            }
        }

        #[tokio::test]
        async fn test_validate_nonce_should_succeed_with_missing_nonce() {
            setup();
            
            let processor = NonceValidationHandler::new();
            let mut context = create_test_context();
            context.measurement.nonce = None;
            context.measurement.nonce_type = Some("ignore".to_string()); // Change to ignore type
            
            let result = processor.handle(&mut context).await;
            
            assert!(result.is_ok());
        }

        #[tokio::test]
        async fn test_get_nonce_validation_processor_name_should_return_valid_name() {
            let processor = NonceValidationHandler::new();
            let name = processor.name();
            assert!(!name.is_empty());
        }
    }

    // Tests for TokenCreation
    mod token_creation_tests {
        use super::*;

        #[tokio::test]
        async fn test_create_token_should_succeed_with_eat_format() {
            setup();
            
            let token_factory = TokenFactory::new();
            let mut context = create_test_context();
            context.measurement.token_fmt = Some("eat".to_string());
            
            let token_result = token_factory.create_token("eat");
            
            // Should create EAT token successfully
            assert!(token_result.is_ok());
        }

        #[tokio::test]
        async fn test_create_token_should_succeed_with_jwt_format() {
            setup();
            
            let token_factory = TokenFactory::new();
            let mut context = create_test_context();
            context.measurement.token_fmt = Some("eat".to_string());
            
            let token_result = token_factory.create_token("eat");
            
            // Should create token successfully
            assert!(token_result.is_ok());
        }

        #[tokio::test]
        async fn test_create_token_should_fail_with_invalid_format() {
            setup();
            
            let token_factory = TokenFactory::new();
            let mut context = create_test_context();
            context.measurement.token_fmt = Some("invalid_format".to_string());
            
            let token_result = token_factory.create_token("invalid_format");
            
            assert!(token_result.is_err());
        }

        #[tokio::test]
        async fn test_create_token_should_succeed_with_missing_format() {
            setup();
            
            let token_factory = TokenFactory::new();
            let mut context = create_test_context();
            context.measurement.token_fmt = None;
            
            let token_result = token_factory.create_token("eat"); // Use default format
            
            assert!(token_result.is_ok());
        }
    }
}